/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package binh;

import binh.data.CUserData;
import binh.packet.CLoginRcvPacket;
import binh.packet.CLoginSentPacket;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import we.common.CDebug;
import we.config.CConstant;
import we.dispatcher.CMessage;
import we.dispatcher.CPacketReader;
import we.dispatcher.CPacketWriter;
import we.dispatcher.CReadHandler;
import we.dispatcher.packet.CEchoRcvPacket;
import we.dispatcher.packet.CEchoSentPacket;
import we.exception.ReadingPacketException;
import we.session.CSession;
import we.session.IoHandlerAdapter;

/**
 *
 * @author ADMIN
 */
public class CBinhHandler extends IoHandlerAdapter
{

    public final static String ATT_CLIENT_INDEX = "ClientIndex";
    private static CBinhHandler _instance;
    //public final static boolean DEBUG_NETWORK = true & CDebug.DEBUG;    
    public final static boolean DEBUG_NETWORK = false;

    public static CBinhHandler Instance()
    {
        if (_instance == null)
        {
            synchronized (CBinhHandler.class)
            {
                if (_instance == null)
                {
                    _instance = new CBinhHandler();
                }
            }
        }
        return _instance;
    }

    private CBinhHandler()
    {
    }

    @Override
    public void MessageReceived(CSession session, Object message)
    {
        super.MessageReceived(session, message);
        CPacketReader reader = (CPacketReader) message;

        if (DEBUG_NETWORK)
        {
            CPacketReader.printBuffRead("receive: ", reader.buffer());
        }
        try
        {
            int CmdID = reader.readID();
            CDebug.Trace(this, " cmd ID : " + CmdID + " - " + CCommon.GetPacketName(CmdID));
            switch (CmdID)
            {
                case 1000:
                    HandleEcho(session, reader);
                    break;
                case CCommon.MT_LOGIN:
                    HandleLogin(session, reader);
                    break;
                case CCommon.MT_REQUEST_SUB_CHANNEL_INFO:
                    HandleRequestSubChannelInfo(session, reader);
                    break;
                case CCommon.MT_REQUEST_LOBBY_INFO:
                    HandleRequestLobbyInfo(session, reader);
                    break;
                case CCommon.MT_JOIN_GAME:
                    HandleRequestJoinGame(session, reader);
                    break;
                case CCommon.MT_CREATE_GAME:
                    HandleRequestCreateGame(session, reader);
                    break;
                case CCommon.MT_QUIT_GAME:
                    HandleQuitGame(session);
                    break;
                case CCommon.MT_REQUEST_USER_LIST:
                    break;
                default:
                    HandleGamePacket(session, CmdID, reader);
                    break;
            }
            reader.clearAll();
        } catch (ReadingPacketException ex)
        {
            Logger.getLogger(CReadHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void SessionOpened(CSession session)
    {
        super.SessionOpened(session);
        CDebug.Trace(this, "Session open. CCU : " + CGlobal.ClientList.GetCCU());

        if (CGlobal.ClientList.AddSlot(session))
        {
        } else
        {
            CDebug.Trace(this, "Full slot, ko the connect dc");
            session.close();
        }
    }

    @Override
    public void Init()
    {
        super.Init();
        CLoopers.Instance().StartLoop();
    }

    @Override
    public void ExceptionCaught(CSession session, Throwable cause)
    {
        super.ExceptionCaught(session, cause);
    }

    public CClient GetClientFromSession(CSession session)
    {
        Integer Index = (Integer) session.getAttribute(ATT_CLIENT_INDEX);
        if (Index == null)
        {
            return null;
        }
        return CGlobal.ClientList.getClient(Index);
    }

    protected boolean JoinChannel(CSession session, int AppId, int ChannelIndex)
    {
        CApp game = CGlobal.AppContainer.GetGameListByID(AppId);
        if (game == null)
        {
            return false;
        }
        CChannel Channel = game.GetChannelByIndex(ChannelIndex);
        CClient Client = GetClientFromSession(session);
        Channel.ClientJoin(Client);
        return false;
    }

    protected void HandleEcho(CSession session, CPacketReader reader)
    {
        CEchoRcvPacket rcvEcho = new CEchoRcvPacket(1000, reader);
        if (!rcvEcho.isValid())
        {
            CDebug.Trace(this, "Loi doc packet");
        } else
        {
            CDebug.Trace(this, "string : " + rcvEcho.echo);
            CEchoSentPacket send = new CEchoSentPacket();
            send.echo = rcvEcho.echo;
            send.processData();
            if (send.isValid())
            {
                session.write(send);
            }
        }
    }

    @Override
    public void MessageSent(CSession session, Object message)
    {

        super.MessageSent(session, message);
        if (DEBUG_NETWORK)
        {
            CMessage msg = (CMessage) message;
            CPacketReader.printBuffWrite("send : ", msg.data);
        }
    }

    @Override
    public void SessionClosed(CSession session)
    {
        CDebug.Trace(this, "Disconnect");
        //CClient 
        CClient Client = GetClientFromSession(session);
        if (Client.GetTable() != null)
        {
            Client.GetTable().SendClientLeft(Client, true);
            Client.GetTable().RemoveClient(Client);
            Client.LeftTable();
        }

        CGlobal.ClientList.removeSlot((Integer) session.getAttribute(ATT_CLIENT_INDEX));

    }

    @Override
    public void SessionCreated(CSession session)
    {
        super.SessionCreated(session);
    }

    @Override
    public void SessionIdle(CSession session)
    {
        super.SessionIdle(session);
    }

    private void HandleLogin(CSession session, CPacketReader reader)
    {
        CClient Client = GetClientFromSession(session);
        CLoginRcvPacket rcvLogin = new CLoginRcvPacket(reader);
        CDebug.Trace(this, "Login : " + rcvLogin.Username + "-" + rcvLogin.Pass);
        // handle Login      
        try
        {
            CUserData Data = CGlobal.Database.login(rcvLogin.Username, rcvLogin.Pass);

            if (Data != null)
            {
                CLoginSentPacket sendLogin = new CLoginSentPacket();
                sendLogin.userID = Data.Id;
                sendLogin.username = Data.Username;
                sendLogin.gold = Data.Gold;
                sendLogin.xu = Data.Ki;
                sendLogin.processData();
                session.write(sendLogin);
                Client.setClientStage(CCommon.CS_LOBBY);
                Client.AccountId = Data.Id;
                Client.Gold = Data.Gold;

            } else
            {
                CLoginSentPacket sendLogin = new CLoginSentPacket();
                sendLogin.error = 2;
                sendLogin.processData();
                session.write(sendLogin);
            }
        } catch (SQLException ex)
        {
            CDebug.Error(this, ex);
        }
    }

    private void HandleRequestSubChannelInfo(CSession session, CPacketReader reader)
    {
        int AppID = reader.buffer().getInt();
        CDebug.Trace(this, "sub Info  appID : " + AppID);

        CApp ChannelList = CGlobal.AppContainer.GetGameListByID(AppID);


        CPacketWriter writer = new CPacketWriter(CConstant.MAX_MESSAGE_LENGTH);
        writer.PutID(CCommon.MT_SUB_CHANNEL_INFO);
        writer.buffer().putInt(ChannelList.GetMaxChannel());
        writer.buffer().putInt(CConfig.MaxClientInChannel);
        for (int i = 0; i < ChannelList.GetMaxChannel(); i++)
        {
            CChannel Channel = ChannelList.GetChannelByIndex(i);
            writer.buffer().putInt(Channel.GetChannelIndex());
            writer.buffer().putInt(Channel.ChannelType);
            writer.buffer().putInt(Channel.GetChannelIndex());
            writer.buffer().putDouble(Channel.MinGoldBet);
            writer.buffer().putDouble(Channel.MaxGoldBet);
            writer.buffer().putDouble(Channel.MinToPlay);
        }
        writer.PutLengthAndFlip();
        session.Send(writer.buffer());
    }

    private void HandleRequestJoinGame(CSession session, CPacketReader reader)
    {
        CClient Client = GetClientFromSession(session);
        if (!Client.IsLogin() || Client.IsInTable())
        {
            return;
        }

        // Lấy thông tin request
        int Error = 0;
        int TableId = reader.GetInt();
        String Password = reader.readString(CCommon.MAX_GAME_PASSWORD_LENGTH);

        CTable Table = Client.GetChannel().GetTableByIndex(TableId);
        if (Table == null)
        {
            return;
        }

        // 2 người cùng IP ko thể vào 1 bàn
        if (Table.CheckSameIP(Client))
        {
            return;
        }

        // Nếu phòng đang chơi, người vào phòng là người xem game
        Client.IsViewer = Table.IsPlaying();

        // Bàn chơi đã đầy
        if (!Table.CanAddClient(Client))
        {
            SendNotifyError(Client, CCommon.ERROR_TABLE_IS_FULL);
            return;
        }


        if (!Table.GameLogic.CanJoinTable(Client))
        {
            return;
        }


        // Không đủ tiền cược để vào bàn
        if (Client.GetGold() < Table.GoldBet * CConfig.MinToPlay)
        {
            SendNotifyError(Client, CCommon.ERROR_NOT_ENOUGH_BET_MONEY);
            return;
        }

        // Send Result
        CPacketWriter p = new CPacketWriter();
        p.PutID(CCommon.MT_JOIN_GAME_RESULT);
        p.PutInt(0);
        p.PutInt(Table.GetAppID());
        p.PutInt(Table.GetChannel().GetChannelIndex());
        p.PutInt(Table.GameId);
        p.PutInt(1);
        p.PutInt(Table.GameStage);
        p.PutInt(0);
        p.PutDouble(Table.GoldBet);
        p.PutInt(Table.ChickenBet);
        p.PutInt(Table.NumChicken);
        p.PutLengthAndFlip();
        Client.Send(p);

        // Add Client
        if (Error == 0)
        {
            if (Client.GetChannel() == null)
            {
                Table.Channel.ClientJoin();
                Client.JoinChannel(Table.Channel);
            } else
            {
                if (Table.Channel != Client.Channel)
                {
                    Client.Channel.ClientLeft();
                    Client.LeftChannel();
                    Table.Channel.ClientJoin();
                    Client.JoinChannel(Table.Channel);
                }
            }

            int Chair = Table.AddClient(Client);
            Client.Chair = Chair;
            Client.JoinTable(Table);
            if (Table.GetNumClient() == 1)
            {
                Table.SetOwner(Client);
            }
            CUserGame UserGame = Client.GetUserGame(Table.GetAppID());
            if (UserGame != null)
            {
                Client.Level = UserGame.Level;
            }

            Table.SendClientJoin(Client);

            // Send Viewer Data
            if (Client.IsViewer)
            {
                Table.GameLogic.SendViewerData(Client);
            }
        }

        // Gửi cập nhật emotion vip
        SendEmotionList(Client);
    }

    private void JoinGame(CClient Client, int TableIndex)
    {
        CChannel Channel = Client.GetChannel();
        // Neu chua vao Channel
        if (Channel == null)
        {
            return;
        }
        CTable Table = Channel.GetTableByIndex(TableIndex);
        if (Table == null)
        {
            return;
        }
        int Chair = Table.AddClient(Client);
        CDebug.Trace(this, "Chair : " + Chair);
        // ko vao duoc ban
        if (Chair < 0)
        {
            return;
        }
        Client.JoinTable(Table);

        CPacketWriter Writer = new CPacketWriter();
        Writer.PutID(CCommon.MT_JOIN_GAME_RESULT);
        Writer.PutInt(0);
        Writer.PutInt(Table.GetAppID());
        Writer.PutInt(Table.GetChannel().GetChannelIndex());
        Writer.PutInt(Table.GetIndex());
        Writer.PutInt(1);
        Writer.PutInt(Table.GameStage);
        Writer.PutInt(0);

        Writer.PutDouble(Table.GoldBet);
        Writer.PutInt((int) Table.ChickenBet);
        Writer.PutInt(Table.NumChicken);
        Writer.PutLengthAndFlip();
        Client.Send(Writer);
    }

    private void HandleRequestLobbyInfo(CSession session, CPacketReader reader)
    {
        CDebug.Trace(this, "Request Lobby Info");

        int AppId = reader.buffer().getInt();
        int ChannelIndex = reader.buffer().getInt();
        int Request = reader.buffer().getInt();
        CDebug.Trace(this, "  app ID     : " + AppId);
        CDebug.Trace(this, "  ChannelID  : " + ChannelIndex);
        CDebug.Trace(this, "  Request    : " + Request);

        CApp ChannelList = CGlobal.AppContainer.GetGameListByID(AppId);
        CChannel Channel = ChannelList.GetChannelByIndex(ChannelIndex);
        CClient Client = GetClientFromSession(session);
        Channel.ClientJoin(Client);
        CDebug.Trace(this, "chennel index : " + Client.GetChannel().GetChannelIndex());
        SendLobbyInfo(Client);
    }

    private void HandleGamePacket(CSession session, int id, CPacketReader reader)
    {
        CClient Client = GetClientFromSession(session);
        Client.GetTable().GetGameLogic().OnReceive(Client, id, reader);
    }

    private void HandleQuitGame(CSession session)
    {
        CClient Client = GetClientFromSession(session);
        if (!Client.IsInTable())
        {
            return;
        }

        // Send Notify
        Client.Table.SendClientLeft(Client, false);

        // Remove Client
        Client.Table.RemoveClient(Client);
        Client.LeftTable();

        // Send Lobby Info
        SendLobbyInfo(Client);
    }

    public void SendLobbyInfo(CClient Client)
    {

        CPacketWriter writer = new CPacketWriter();
        writer.PutID(CCommon.MT_LOBBY_INFO);
        int error = 0;
        writer.buffer().putInt(error);
        writer.buffer().putInt(Client.GetChannel().GetChannelIndex());
        int maxTableInRoom = CConfig.MAX_TABLE_IN_CHANNEL;
        writer.buffer().putInt(maxTableInRoom);
        writer.buffer().putInt(0);
        for (int i = 0; i < maxTableInRoom; i++)
        {
            CTable Table = Client.GetChannel().GetTableByIndex(i);
            writer.buffer().putInt(Table.GetIndex());
            writer.buffer().put((byte) Table.GetNumClient());
            writer.buffer().putInt((int) Table.GoldBet);
            writer.buffer().putInt(Table.ChickenBet);

        }

        writer.buffer().putInt(4);
        writer.buffer().putInt(10);
        for (int j = 0; j < 10; j++)
        {
            writer.buffer().putInt(j);
            writer.buffer().putInt(2);
        }

        writer.PutLengthAndFlip();
        Client.GetSession().Send(writer.buffer());
    }

    private void HandleRequestCreateGame(CSession session, CPacketReader reader)
    {
        int AppId = reader.GetInt();
        int TableId = reader.GetInt();
        int ChannelId = reader.GetInt();
        int GameMode = reader.GetInt();
        double GoldBet = reader.GetDouble();
        CDebug.Trace(this, "App ID        : " + AppId);
        CDebug.Trace(this, "TableID       : " + TableId);
        CDebug.Trace(this, "ChannelID     : " + ChannelId);
        CDebug.Trace(this, "Game Mode     : " + GameMode);
        CClient Client = GetClientFromSession(session);

        if (!Client.IsLogin() || Client.IsInTable())
        {
            return;
        }

        // Lấy thông tin bàn chơi

        CTable Table = Client.GetChannel().GetTableByIndex(TableId);
        if (Table == null)
        {
            return;
        }

        // Bàn chơi đã có người
        if (Table.GameStage != CCommon.GS_WAITING_ROOM || Table.GetNumClient() > 0)
        {
            SendNotifyError(Client, CCommon.ERROR_TABLE_HAS_CLIENT);
            SendLobbyInfo(Client);
            return;
        }

        // Thiếu tiền đặt cược
        if (Client.GetGold() < Table.GoldBet * CConfig.MinToPlay)
        {
            SendNotifyError(Client, CCommon.ERROR_NOT_ENOUGH_BET_MONEY);
            return;
        }

        // Tạo bàn chơi
        Table.CreateGame(GoldBet);
        int Chair = Table.AddClient(Client);
        Client.JoinTable(Table);
        Table.SetOwner(Client);
        Client.Chair = Chair;
        Client.IsViewer = false;
        CUserGame UserGame = Client.GetUserGame(Table.GetAppID());
        if (UserGame != null)
        {
            Client.Level = UserGame.Level;
        }

        // Send Result
        CPacketWriter p = new CPacketWriter();
        p.PutID(CCommon.MT_CREATE_GAME_RESULT);
        p.PutInt(0);
        p.PutInt(AppId);
        p.PutInt(Table.Channel.GetChannelIndex());
        p.PutInt(Table.GameId);
        p.PutInt(GameMode);
        p.PutDouble(Table.GoldBet);
        p.PutInt(Table.ChickenBet);
        p.PutInt(Table.NumChicken);
        p.PutLengthAndFlip();
        Client.Send(p);

        // Send Client Info
        Table.SendClientJoin(Client);

        // Gửi cập nhật emotion vip
        SendEmotionList(Client);
    }

    private void SendNotifyError(CClient Client, int ErrorId)
    {
        CPacketWriter Packet = new CPacketWriter(CCommon.MT_NOTIFY_ERROR);
        Packet.PutInt(ErrorId);
        Packet.PutLengthAndFlip();
        Client.Send(Packet);
    }

    private void SendEmotionList(CClient Client)
    {
        // throw new UnsupportedOperationException("Not yet implemented");
        // TODO
    }
}
